<template>
  <my-page title="十六进制编辑器" :page="page">
    <div class="editor-box" :style="{ right: toolBoxWidth + 'px' }">
      <div class="editor-box-content">
        <input type="file" class="inputfile" @change="fileChange($event)" />
        <div class="loading" v-if="loading">读取中...</div>
        <div class="data-box">
          <div class="list">
            <!-- <table border="0">
                <thead>
                    <tr>
                        <td>
                            <pre>序列</pre>
                        </td>
                        <td id='t'></td>
                        <td>序列简图</td>
                    </tr>
                </thead>
                <tr>
                    <td id="l" width="80">00000000</td>
                    <td>
                        <textarea spellcheck=false id="m" placeholder="请输入" @input='textareaInput($event)' cols=48></textarea>
                    </td>
                    <td width="160" id="r">.</td>
                </tr>
            </table> -->
            <div class="list-row data-header">
              <div class="grid grid-offset">序列</div>
              <div class="grid grid-data grid-data-header">
                <div class="grid grid-ceil" v-for="(item, index) in headers" :key="index">
                  {{ item }}
                </div>
              </div>
              <div class="grid grid-asc">--</div>
            </div>
            <div class="list-row-box">
              <div class="list-row list-row-offset">
                <div
                  class="grid grid-offset"
                  v-for="(row, index) in displayResult"
                  :key="index"
                >
                  {{ offset(index) }}
                </div>
              </div>
              <div class="list-row list-row-hex">
                <div
                  class="grid grid-data"
                  v-for="(row, index) in displayResult"
                  :key="index"
                >
                  <span
                    class="grid grid-ceil bg_ffbf"
                    v-for="(item, column) in row"
                    :key="column"
                    :class="ceilClass(index, column)"
                    @mousedown="ceilMouseDown($event, index, column)"
                    @mousemove="ceilMouseMove($event, index, column)"
                    @mouseup="ceilMouseUp($event, index, column)"
                    @click="select(index, column)"
                  >
                    {{ hex(item) }}
                  </span>
                  <!-- <input type="text"
                    class="grid grid-ceil bg_ffbf input_none"
                    v-for="(item, column) in row"
                    :key="column"
                    maxlength="2"
                    :value="hex(item)"
                    @mousedown="ceilMouseDown($event, index, column)"
                    @mousemove="ceilMouseMove($event, index, column)"
                    @mouseup="ceilMouseUp($event, index, column)"
                    @input="ceilInputOn($event, index, column)"
                    @click="select(index, column)"
                    /> -->
                </div>
              </div>
              <div class="list-row list-row-asc">
                <div class="asc" v-for="(row, index) in displayResult" :key="index">
                  <span v-for="(item, column) in ascArr(row)" :key="column">
                    <span
                      class="asc-ceil"
                      :class="ascCeilClass(index, column)"
                      @click="select(index, column)"
                      @mousedown="ceilMouseDown($event, index, column)"
                      @mousemove="ceilMouseMove($event, index, column)"
                      @mouseup="ceilMouseUp($event, index, column)"
                      >
                      {{ item }}
                      <!-- . -->
                    </span>
                  </span>
                </div>
              </div>
            </div>
          </div>
          <div class="btns">
            <ui-raised-button class="btn" label="上一页" @click="prevPage" />
            <ui-raised-button class="btn" :disabled="displayResult.length === 0" label="下一页" @click="nextPage" />
            <ui-raised-button class="btn" label="下载" @click="download" />
          </div>
          <div>
            <ui-slider
              class="slider"
              v-model="dataOffset"
              :step="1"
              :min="0"
              :display-value="false"
              :max="result.length"
              v-if="result"
            />
          </div>
        </div>
      </div>
    </div>
    <div class="tool-box" :style="{ width: toolBoxWidth + 'px' }" v-if="displayResult">
      <div class="handler" @mousedown="handlerMouseDown($event)"></div>
      <div class="search-box">
        <input
          class="input"
          v-model="keyword"
          placeholder="搜索十六进制编码、ASCII"
        />
        <ui-icon-button icon="search" label="搜索" @click="search" />
      </div>
      <ui-article class="article">
        <table>
          <tr>
            <th>十六进制</th>
            <td>
              <!--{{ ceilHex }}-->
              <ui-select-field
                class="hex-select"
                v-model="ceilHex1"
                @change="onEdit"
              >
                <ui-menu-item
                  :value="item"
                  :title="item"
                  v-for="item in hexArr"
                  :key="item"
                />
              </ui-select-field>
              <ui-select-field
                class="hex-select"
                v-model="ceilHex2"
                @change="onEdit"
              >
                <ui-menu-item
                  :value="item"
                  :title="item"
                  v-for="item in hexArr"
                  :key="item"
                />
              </ui-select-field>
            </td>
          </tr>
          <tr>
            <th>二进制（8位）</th>
            <td>{{ ceilBinary }}</td>
          </tr>
          <tr>
            <th>Int8</th>
            <td>{{ ceil }}</td>
          </tr>
          <tr>
            <th>UInt8</th>
            <td>{{ ceil }}</td>
          </tr>
          <tr>
            <th>ASCII</th>
            <td>{{ ceilAsc }}</td>
          </tr>
        </table>
      </ui-article>
    </div>
  </my-page>
</template>

<script>
/* eslint-disable */
// var doubleConversion = require("double-conversion-decimal-binary");

if (!String.prototype.padStart) {
  String.prototype.padStart = function padStart(targetLength, padString) {
    targetLength = targetLength >> 0; //truncate if number or convert non-number to 0;
    padString = String(typeof padString !== "undefined" ? padString : " ");
    if (this.length > targetLength) {
      return String(this);
    } else {
      targetLength = targetLength - this.length;
      if (targetLength > padString.length) {
        padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
      }
      return padString.slice(0, targetLength) + String(this);
    }
  };
}

export default {
  data() {
    return {
      hexArr: [
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "A",
        "B",
        "C",
        "D",
        "E",
        "F",
      ],
      ceilHex1: "1",
      ceilHex2: "2",
      dataOffset: 0,
      //                toolBoxWidth: 256,
      toolBoxWidth: 400,
      // selection
      startPosition: 0,
      endPosition: 0,
      loading: false,
      headers: [
        "00",
        "01",
        "02",
        "03",
        "04",
        "05",
        "06",
        "07",
        "08",
        "09",
        "0A",
        "0B",
        "0C",
        "0D",
        "0E",
        "0F",
      ],
      //                result: null,
      displayResult: null,
      //                ceil: null,
      ceil: 71,
      // search
      keyword: "",
      m: {value: '00'},
      h: '',
      c: null,
      t: null,
      l: null
      //                page: {
      //                    menu: [
      //                        {
      //                            type: 'icon',
      //                            icon: 'language',
      //                            to: '/ex',
      //                            title: '语言'
      //                        }
      //                    ]
      //                }
    };
  },
  computed: {
    ceilBinary() {
      return this.ceil.toString(2).padStart(8, "0");
    },
    ceilAsc() {
      return String.fromCharCode(this.ceil);
    },
    ceilHex() {
      return this.ceil.toString(16).toUpperCase();
    },
  },
  mounted() {
    // this.load_init();
    this.debug();
  },
  methods: {
    load_init () {
        this.t = document.getElementById('t');
        this.l = document.getElementById('l');
        console.dir(this.t)
        // 重置文本框内容
        this.m.value = "00";
        // 初始化序列表头
        for (let i = 0; i < 16; i++) {
            this.t.innerHTML += (0 + i.toString(16)).slice(-2).toUpperCase() + " ";
        }
    },
    textareaInput(event) {
        let dom = document.getElementById('m')
        let value = event.target.value
        console.dir(value)
        // On input, store the length of clean hex before the textarea caret in b
        let b = value
            .substr(0, value.length)
            .replace(/[^0-9A-F]/ig, "")
            .replace(/(..)/g, "$1 ")
            .length;

        // Clean the textarea value
        dom.value = value
            .replace(/[^0-9A-F]/ig, "")
            .replace(/(..)/g, "$1 ")
            .replace(/ $/, "")
            .toUpperCase();
        // Set the height of the textarea according to its length
        dom.style.height = (1.5 + value.length / 47) + "em";
        // Reset h
        this.h = "";
        // Loop on textarea lines
        for (let i = 0; i < value.length / 48; i++) {
            // Add line number to h
            this.h += (1E7 + (16 * i).toString(16)).slice(-8) + " ";
        }
        // Write h on the left column
        l.innerHTML = this.h;
        // Reset h
        this.h = "";
        // Loop on the hex values
        for (let i = 0; i < value.length; i += 3) {
            // Convert them in numbers
            this.c = parseInt(value.substr(i, 2), 16);
            // Convert in chars (if the charCode is in [64-126] (maybe more later)) or ".".
            this.h = 63 < this.c && 127 > this.c ? this.h + String.fromCharCode(this.c) : this.h + ".";
        }

        // Write h in the right column (with line breaks every 16 chars)
        r.innerHTML = this.h.replace(/(.{16})/g, "$1 ");

        // If the caret position is after a space or a line break, place it at the previous index so we can use backspace to erase hex code
        if (value[b] === " ") {
            b--;
        }
    },
    debug() {
      let array = new Uint8Array([0, 10, 12, 14, 1, 3, 6, 56, 57, 89, 78, 102, 900, 12, 19]);
      this.loadData(array);
    },
    prevPage(e) {
      this.dataOffset -= 20;
      if (this.dataOffset < 0) {
        this.dataOffset = 0;
      }
      this.loadPage();
    },
    nextPage(e) {
      this.dataOffset += 20;
      this.loadPage();
    },
    handlerMouseDown(e) {
      let downX = e.pageX;
      let onMouseMove;
      let originWidth = this.toolBoxWidth;
      document.body.setAttribute("user-select", "none");
      document.addEventListener(
        "mousemove",
        (onMouseMove = (e) => {
          this.toolBoxWidth = originWidth - e.pageX + downX;
          if (this.toolBoxWidth < 256) {
            this.toolBoxWidth = 256;
          }
        })
      );
      document.addEventListener("mouseup", (e) => {
        document.removeEventListener("mousemove", onMouseMove);
        document.body.setAttribute("user-select", "inherit");
      });
    },
    ceilMouseDown(e, row, column) {
      this.ceilDown = true;
      let position = (this.dataOffset + row) * 16 + column;
      this.startPosition = position;
      this.endPosition = position;
    },
    ceilMouseUp(e, row, column) {
      this.ceilDown = false;
    },
    ceilMouseMove(e, row, column) {
      if (this.ceilDown) {
        let position = (this.dataOffset + row) * 16 + column;
        this.endPosition = position;
      }
    },
    ceilClass(row, column) {
      let position = (this.dataOffset + row) * 16 + column;
      if (position >= this.startPosition && position <= this.endPosition) {
        return ["active"];
      }
      return [];
    },
    ascCeilClass(row, column) {
      let position = (this.dataOffset + row) * 16 + column;
      if (position >= this.startPosition && position <= this.endPosition) {
        return ["active"];
      }
      return [];
    },
    select(row, column) {
      // console.log(row, column)
    //   let position = (this.dataOffset + row) * 16 + column;
      this.selectedRow = row;
      this.selectedColumn = column;
      this.ceil = this.displayResult[row][column];
      this.ceilHex1 = this.ceil.toString(16).charAt(0).toUpperCase();
      this.ceilHex2 = this.ceil.toString(16).charAt(1).toUpperCase();
    },
    ascArr(row) {
      return row.map((item) => {
        let ret = String.fromCharCode(item);
        // console.log(ret.replace(/(^s*)|(s*$)/g, "").length)
        ret = this.character(ret)
        if (ret === 'null') {
          return ".";
        }
        return ret;
      });
    },
    // 转义控制字符
    character(str) {
      return str.replace(/\u0000|\u0001|\u0002|\u0003|\u0004|\u0005|\u0006|\u0007|\u0008|\u0009|\u000a|\u000b|\u000c|\u000d|\u000e|\u000f|\u0010|\u0011|\u0012|\u0013|\u0014|\u0015|\u0016|\u0017|\u0018|\u0019|\u001a|\u001b|\u001c|\u001d|\u001e|\u001f/g,"null");
    },
    fileChange(e) {
      this.loading = true;
      this.result = null;
      let file = e.target.files[0];
      this.fileName = file.name;
      this.fileType = file.type;

      let reader = new FileReader();
      this.startTime = new Date().getTime();
      reader.onload = (e) => {
        let array = new Uint8Array(e.target.result);
        this.loadData(array);
      };
      reader.readAsArrayBuffer(file);
    },
    hex(item) {
      return item.toString(16).toUpperCase();
    },
    loadData(array) {
      this.array = array;
      let ret = [];
      let intArr = [];
      let tempArr = [];
      for (let i = 0; i < array.length; i++) {
        if (i % 16 === 0) {
          tempArr = [];
        }
        intArr.push(array[i]);
        // console.log(array[i].toString(16))
        let n = array[i].toString(16).toUpperCase();
        let s = "00" + n;

        tempArr.push(s.substr(n.length, s.length));
        if (i % 16 === 15) {
          ret.push(tempArr);
        }
      }
      // console.log(tempArr)
      ret.push(tempArr);
      this.result = ret;
      this.loadPage();
      this.loading = false;
      // let loadTime = ((new Date().getTime() - this.startTime) / 1000).toFixed(
      //   2
      // );
      // console.log(`加载了 ${loadTime}ms`);
    },
    search() {
      if (!this.keyword) {
        this.$message({
          type: "danger",
          text: "请输入关键词",
        });
        return;
      }

      let startTime = new Date().getTime();
      let ascText = "";
      let hexText = "";
      for (let i = 0; i < this.array.length; i++) {
        ascText += String.fromCharCode(this.array[i]);
        hexText += this.array[i].toString(16).toUpperCase();
      }
      console.log(ascText.length);
      console.log(new Date().getTime() - startTime);

      let idx = ascText.indexOf(this.keyword); // toUpperCase
      console.log(idx);
      if (idx !== -1) {
        console.log("找到了" + idx);
        this.dataOffset = idx / 16;
        this.startPosition = this.endPosition = idx;
        console.log(this.dataOffset);
        this.loadPage();
      } else {
        idx = hexText.indexOf(this.keyword.toUpperCase()); // TODO
        if (idx !== -1) {
          console.log("找到了" + idx);
          this.dataOffset = idx / 16 / 2 + 1;
          console.log(this.dataOffset);
          this.loadPage();
        } else {
          this.$message({
            type: "danger",
            text: "搜索不到文本",
          });
          console.log("找不到");
        }
      }
    },
    offset(index) {
      return ((this.dataOffset + index) * 16)
        .toString(16)
        .toUpperCase()
        .padStart(8, "0");
    },
    init() {},
    loadPage() {
      // console.log(this.result)
      // this.displayResult = this.result; // 展示全部
      this.displayResult = this.result.slice(
        this.dataOffset,
        this.dataOffset + 30
      );
      // if (this.displayResult)
      // console.log(this.displayResult)
      if (this.displayResult.length > 0) {
        this.select(0, 0);
      }
    },
    download() {
      console.log(this.array);
      var b64 = btoa(String.fromCharCode.apply(null, this.array));

      var a = document.createElement("a");
      a.style = "display: none";
      let fileType = this.fileType || "application/octet-stream";
      a.setAttribute("download", this.fileName);
      a.href = "data:" + fileType + ";base64," + b64;

      document.body.appendChild(a);
      a.click();
    },
    onEdit() {
      this.$nextTick(() => {
        console.log("改变了", this.ceilHex1 + this.ceilHex2);
        let num = parseInt(this.ceilHex1 + this.ceilHex2, 16);
        console.log(num);
        this.array[this.startPosition] = num;
        this.ceil = num;
        this.displayResult[this.selectedRow][this.selectedColumn] = num;
      });
    },
    ceilInputOn(e, row, column) {
        console.dir(row, column)
        console.log(this.displayResult)
        let isHex = /^[A-Fa-f0-9]{1,4}$/.test(e.data)
        if (!isHex) {
            // 不是十六进制
            return
        } else {
            return e.data.toUpperCase().substring(0, 2)
        }
    }
  },
  watch: {
    dataOffset() {
      this.loadPage();
    },
    ceilHex1() {
      //                this.onEdit()
    },
    ceilHex2() {
      //                this.onEdit()
    },
  },
};
</script>

<style lang="scss" scoped>
// --
textarea {
    border: 0;
}
textarea:focus {
    outline: none !important;
    border: 0;
}
#m {
    height: 1.5em;
    resize: none;
    // overflow: hidden
}
#t {
    padding: 0 2px
}
#r {
    font-size: 18px;
}
// ---
.editor-box {
  position: absolute;
  top: 0;
  left: 0;
  right: 256px;
  bottom: 0;
  overflow: auto;
}
.editor-box-content {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 950px;
  padding: 16px;
}
.tool-box {
  position: absolute;
  top: 0;
  right: 0;
  width: 256px;
  bottom: 0;
  border-left: 1px solid rgba(0, 0, 0, 0.12);
  .handler {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    width: 5px;
    /*background-color: #ccc;*/
    cursor: col-resize;
  }
  .article {
    padding: 16px;
  }
  table {
    width: 100%;
  }
}
.search-box {
  display: flex;
  input {
    height: 48px;
    flex-grow: 1;
    outline: none;
    border: none;
    padding-left: 16px;
  }
  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
.list {
  height: 560px;
  margin-top: 24px;
  /*max-width: 640px;*/
  user-select: none;
  overflow: auto;
}
.list-row-box {
  display: flex;
}
.list-row {
  margin-bottom: 4px;
  overflow: hidden;
}
.grid {
  float: left;
  /*display: inline-block;*/
  width: 32px;
}
.bg_ffbf {
    background: #FFEEBF;
}
.input_none {
    border: 0;
}
.input_none:focus {
    outline: none;
    border: 0;
}
.grid-ceil {
  text-align: center;
  cursor: pointer;
  /*border: 1px solid #eee;*/
  &:hover {
    background-color: #eee;
  }
  &.active {
    color: #fff;
    background-color: #4285f4;
  }
}
.grid-data {
  width: 560px;
  max-width: 640px;
  margin-right: 0px;
  overflow: hidden;
}
.grid-data-header {
  color: #999;
}
.data-header {
  color: #999;
  /*text-align: center;*/
}
.list-row-offset {
  width: 80px;
}
.list-row-hex {
  width: 560px;
}
.list-row-asc {
  /*width: 560px;*/
}
.grid-offset {
  width: 80px;
  color: #999;
}
.asc-ceil {
  display: inline-block;
  width: 16px;
  text-align: center;
  cursor: pointer;
  &:hover {
    background-color: #eee;
  }
  &.active {
    color: #fff;
    background-color: #4285f4;
  }
}
.loading {
  margin: 16px 0;
}
.btns {
  margin-top: 16px;
  margin-bottom: 16px;
  .btn {
    margin-right: 8px;
  }
}
.hex-select {
  width: 64px;
  &:first-child {
    margin-right: 8px;
  }
}
.inputfile {
  width: 200px;
  height: 30px;
  z-index: 1;
}
.inputfile + label {
    font-size: 1.25em;
    font-weight: 700;
    color: white;
    background-color: black;
    display: inline-block;
}

.inputfile:focus + label,
.inputfile + label:hover {
    background-color: red;
}
.inputfile + label {
  cursor: pointer; /* 小手光标*/
}
</style>
